package column_generation;

import Jama.Matrix;
import java.io.*;
import java.util.Arrays;
import java.util.ArrayList;

public class DataReader {

    public int stockLength;
    public int[] specs;
    public double[] requirements;

    public Matrix C;
    public Matrix A;
    public Matrix b;

    public void formulateModel() {

        int specNum = specs.length;

        Arrays.sort(specs);

        int[] max = new int[specNum]; // 每种长度可切的最大根数
        for (int i = 0; i < specNum; i++) {
            max[i] = (int) stockLength / specs[i];
        }

        // 构造所有可能性的集合
        int[][] allCases = new int[specNum][];
        for (int i = 0; i < specNum; i++) {
            allCases[i] = range(max[i]);
        }

        int[] p = new int[specNum]; // 定义指向每一个长度的当前根数的指针
        ArrayList<int[]> result = new ArrayList<int[]>(); // 存放所有结果

        // 穷举构造所有切割方案，采用specNum个指针分别指向每一个spec当前切割的根数
        // 并通过类似时钟时针、分针、秒针的方式操作每一个指针
        // 控制指针0遍历所有情况，其余指针保持不动，当指针0遍历完成时，指针1指向下一位置, 指针0重新指向初始位置
        // 当指针1遍历完所有情况时（此时指针0已完成指针1指向列表的长度次遍历）, 指针2指向下一位置，指针1和0指向初始位置
        // 以此类推，当所有指针均指向最后一位时，完成穷举
        while (true) {
            while (p[0] != max[0] + 1) {
                int[] temp = new int[specNum];
                for (int i = 0; i < specNum; i++) {
                    temp[i] = allCases[i][p[i]];
                }
                if (isFeasible(temp)) {
                    result.add(temp);
                }
                p[0]++;
            }
            if (isFinished(p, max)) {
                break;
            }
            p[0] = 0;
            boolean flag = true; // 定义进位标志位
            for (int i = 1; i < specNum; i++) {
                if (flag) {
                    p[i]++;
                    if (p[i] == max[i] + 1) {
                        p[i] = 0;
                        flag = true;
                    } else {
                        flag = false;
                    }
                }
            }
        }

        // 判断是否存在初始基可行解
        // 理论上存在只切割一个spec的方案，可以构成初始基可行解，但这种方案可能不优被舍弃了
        // 所以现在要把舍弃的方案拿回来以构造初始基可行解（虽然这个方案一定不会在最优解里）
        int[] base = new int[specNum];
        for (int[] i : result) {
            int temp = 0; // 记录i中不为0的数
            int index = -1; // 记录i中不为0的index
            for (int j = 0; j < i.length; j++) {
                if (i[j] != 0) {
                    temp++;
                    if (temp >= 2) {
                        index = -1;
                        break;
                    } else {
                        index = j;
                    }
                }
            }
            if (index != -1) {
                base[index] = 1;
            }
        }
        for (int i = 0; i < specNum; i++) {
            if (base[i] == 0) {
                int[] temp = new int[specNum];
                temp[i] = (int) stockLength / specs[i];
                result.add(temp);
            }
        }
        int resultNum = result.size();

        // 生成AbC
        double[][] a_list = new double[specNum][resultNum];
        for (int i = 0; i < specNum; i++) {
            for (int j = 0; j < resultNum; j++) {
                a_list[i][j] = result.get(j)[i];
            }
        }
        A = new Matrix(a_list);

        b = new Matrix(requirements, 1).transpose();
        double[] temp = new double[resultNum];
        for (int i = 0; i < resultNum; i++) {
            temp[i] = 1;
        }
        C = new Matrix(temp, 1);
        // A.print(7, 1);
        // b.print(1, 1);
        // C.print(7, 1);
    }

    // 保留该解的条件：1. 总长度不超过限制 2. 剩余物料长度不超过最小spec
    private boolean isFeasible(int[] s) {
        int temp = 0;
        for (int i = 0; i < s.length; i++) {
            temp += s[i] * specs[i];
            if (temp > stockLength) {
                return false;
            }
        }

        if (stockLength - temp >= specs[0]) {
            return false;
        }

        return true;
    }

    // 跳出条件：p[i] == max[i], i != 0
    private boolean isFinished(int[] p, int[] max) {
        for (int i = 1; i < p.length; i++) {
            if (p[i] != max[i]) {
                return false;
            }
        }
        return true;
    }

    private int[] range(int n) {
        int[] temp = new int[n + 1];
        for (int i = 0; i < n + 1; i++) {
            temp[i] = i;
        }
        return temp;
    }

    public void loadFile(String path) {
        try {
            FileReader fr = new FileReader(path);
            BufferedReader br = new BufferedReader(fr);
            String line;
            String[] temp;
            int i = 0;
            while ((line = br.readLine()) != null) {

                if (i == 0) {

                    stockLength = Integer.parseInt(line);

                } else if (i == 1) {

                    temp = line.substring(1, line.length() - 1).split(",");
                    specs = new int[temp.length];
                    for (int j = 0; j < temp.length; j++) {
                        specs[j] = Integer.valueOf(temp[j]);
                    }

                } else if (i == 2) {

                    temp = line.substring(1, line.length() - 1).split(",");
                    requirements = new double[temp.length];
                    for (int j = 0; j < temp.length; j++) {
                        requirements[j] = Double.parseDouble(temp[j]);
                    }

                }
                i++;

            }
            br.close();
        } catch (IOException e) {
            System.out.println(e);
        }
    }
}